#include "perlinNoise.h"

// simply power
float power(float a, int x){
	float ret = a;
	for(int i=0; i<x-1; i++) ret*=a;
	return ret;
}

// pseudo-random noise
double noise(double x,double y){
 int n=(int)x+(int)y*120;
 n=(n<<13)^n;
 int nn=(n*(n*n*60493+19990303)+1376312589)&0x7fffffff;
 return 1.0-((double)nn/1073741824.0);
}

// cosine interpolation between two points
double interpolate(double a,double b,double x){
 double ft=x * 3.1415927;
 double f=(1.0-cos(ft))* 0.5;
 return a*(1.0-f)+b*f;
}

// Perlin noise for specified point (-1.0 to 1.0 in return)
double perlin2D(double x,double y){
 double floorx=(double)((int)x);
 double floory=(double)((int)y);
 double s,t,u,v;
 s=noise(floorx,floory); 
 t=noise(floorx+1,floory);
 u=noise(floorx,floory+1);
 v=noise(floorx+1,floory+1);
 double int1=interpolate(s,t,x-floorx);
 double int2=interpolate(u,v,x-floorx);
 return interpolate(int1,int2,y-floory);
}

// cloudy image bitmap
ALLEGRO_BITMAP* getPerlinNoise(int aW, int aH, double aZoom, double aP, int aOctaves, int aSeed){
	int octaves = aOctaves;
	int w = aW;
	int h = aH;
	double zoom = aZoom; //fractal zoom
	double p = aP; //persistence

	ALLEGRO_BITMAP* disp = al_get_target_bitmap();
	ALLEGRO_BITMAP* perlin = al_create_bitmap(w,h);
	al_set_target_bitmap(perlin);
	
	al_lock_bitmap(perlin,ALLEGRO_PIXEL_FORMAT_ANY,ALLEGRO_LOCK_WRITEONLY);
	// Perlin noise generation
	int seedX;
	int seedY;
	if(aSeed != 0){
		seedX = noise(aSeed,aSeed);
		seedY = noise(aSeed+1,aSeed+1);
	}
	else{
		seedX = rand();
	  seedY = rand();
	}
	for(int y=seedY; y<h+seedY; y++){
		for(int x=seedX; x<w+seedX; x++){
			double getnoise = 0;
			for(int a=0; a<octaves-1; a++){
				double frequency = power(2,a);
				double amplitude = power(p,a);
				getnoise += perlin2D(((double)x)*frequency/zoom,((double)y)/zoom*frequency)*amplitude;
			}

			// conversion form (-1.0; 1.0) to (0, 255);
			int color = (int)((getnoise*128.0)+128.0);
			if(color>255) color=255;
			if(color<0) color=0;
			al_put_pixel(x-seedX,y-seedY,al_map_rgb(color,color,color));
		}
	}
	al_unlock_bitmap(perlin);

	al_set_target_bitmap(disp);
	return perlin;
}

int getPixel(ALLEGRO_BITMAP* aImg, int aX, int aY){
	if(aX < 0 || aY < 0 || aX >= al_get_bitmap_width(aImg) || aY >= al_get_bitmap_height(aImg)) return -1;
	unsigned char color = 0;
	al_unmap_rgb(al_get_pixel(aImg,aX,aY),&color,&color,&color);
	return (int)color;
}